//多态
//1.向上转型
//2.子类和父类有同名的（覆盖）/重写方法
//3.通过父类对象调用父类和子类的重写的方法
//4.满足以上3点要求，只能说会发生动态绑定
//5.什么是动态绑定，什么又是静态绑定
//6.理解上述5点你才能可理解什么是多态，其实多态就是一种思想，但是非常抽象
class Animals{
    public String name;
    public int age;

    public Animals(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }

    @Override
    public String toString() {
        return "Animals{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}
class Dog extends Animals{
    public Dog(String name, int age) {
        super(name, age);
    }

    public void wangWang(){
        System.out.println(this.name + "正在狂吠了");
    }
    public void eat(){
        System.out.println(this.name + "正在吃狗粮！！！");
    }
}
class Birds extends Animals{
    public Birds(String name, int age) {
        super(name, age);
    }
    public void fly(){
        System.out.println(this.name + "正在飞");
    }
}

public class TestDemo {
    public static void main1(String[] args) {
        Dog dog = new Dog("小狗",19);
        dog.eat();
        dog.wangWang();
        System.out.println("============");
        Animals animals = new Animals("animals" ,8);
        animals.eat();
        //animals.wangWang();//这个时候我们发现了这个animals无法调用wangWang的这个方法
        //只能访问这个animals的这个类的对象

    }

    //向上转型
    //语法格式：
    //父类类型 对象名 = new 子类类型()

    //向上转型的优点：让代码实现更简单灵活。
    //向上转型的缺陷：不能调用到子类特有的方法。

    /**
     * 有3种方式可以发生向上转型
     * 1.直接赋值
     * 直接把子类对象给父类去进行引用
     * 2.方法传参的方式
     * 3.返回值的方式
     * @param args
     */
    //直接赋值
    public static void main2(String[] args) {
        //向上转型：子类类型给父类类型
        /*Dog dog = new Dog("小狗",19);


        Animals animals = dog;*/
        //其实这两个操作就是向上转型，那么合二为一就是以下操作
        //其实就是父类引用，引用了子类的对象
        Animals animals1 = new Dog("小狗",19);

    }
    //方法传参
    public static void main3(String[] args) {
        Animals animals1 = new Dog("小狗",10);
        Animals animals2 = new Birds("小鸟",10);
        Dog dog = new Dog("小狗",10);
        Birds birds = new Birds("小鸟",10);
        func1(dog);
        func1(birds);//这个参数是父类类型，但是传的参数是子类类型
    }

    public static void func1(Animals animals) {
        //所以我们会觉得传递的参数的数据类型不应该是匹配的吗？
        //但是这个时候就是发生了向上转型
        //因为子类继承了父类，父类引用子类的对象
    }

    //返回值
    public static void main4(String[] args) {
        Animals animals = func2();//返回的是birds或者dog都可以
        //所以返回的是它的子类或者它本身的类型都可以
        Animals animals1 = func3();
    }
    public static Animals func2(){//这个类型就是Animals类型
        //return new Birds("小鸟",10);
        return new Dog("小狗",10);

    }
    //当然我们还可以进行写其他类类型
    public static Birds func3(){
        return new Birds("小鸟",10);
        //return new Dog("小鸟",10) ;
        //这个时候我们那就不能改变了为其他的类类型了，比如dog或者Animals类型都不可以
        //所以我们建议还是用来写父类的类型做方法的类型比较好
    }

    public static void main(String[] args) {
        Animals animals = new Dog("小狗",10);//向上转型，只能调用父类的被子类继承的方法。当然子类继承的都是非私有的成员变量和方法
        //animals.wangWang();//向上转型的缺陷：不能调用到子类特有的方法。
        animals.eat();//父类中所具有的方法
        //这个时候eat是被指在子类中所写的方法
        //通过父类的引用，来调用了父类的方法和子类的重写方法，但是最后调用的是子类中重写的方法。
        //这个过程就叫做：运行时绑定 或 动态绑定

        //通过对于这种情况用javap -c TestDemo来查看，我们发现的确在编译的时候告诉我们调用的是父类的eat
        //但是运行时发现子类重写父类的eat方法，所以会直接调用子类的

        //所以重写了，就必须得调用自己的，在类中，这种叫做动态绑定
    }
    //方法重写（覆盖，覆写）
    //1.方法名一样
    //2.参数列表一样（个数，类型，顺序）
    //3.返回值一样
}
//静态的绑定/编译时绑定：方法的重载，在编译的时候，就能确定他调用的是哪个方法了